// noinspection JSUnusedGlobalSymbols

export function* range(start: number, end?: number, step: number = 1): Generator<number> {
    if (end === undefined) {
        end = start;
        start = 0;
    }
    for (let i = start; i !== end; i += step) {
        yield i;
    }
}

export function rangeArray(start: number, end?: number, step: number = 1): number[] {
    return Array.from(range(start, end, step));
}

export function* reversed<T>(it: Iterable<T>) {
    const arr = Array.from(it);
    for (const i of range(arr.length - 1, -1, -1)) {
        yield arr[i];
    }
}

export function* pairwise<T>(arr: T[], n: number = 2): Generator<T[]> {
    for (let i = 0; i <= arr.length - n; i++) {
        yield arr.slice(i, i + n);
    }
}

export type Position = [number, number];

export function distanceRange(fromDis: number, toDis: number): Generator<number> {
    if (fromDis <= toDis) {
        return range(fromDis, toDis + 1);
    } else {
        return range(fromDis, toDis - 1, -1);
    }
}

export function* spiralIter8d([x, y]: Position, fromDis: number, toDis: number): Generator<Position> {
    for (const d of distanceRange(fromDis, toDis)) {
        if (d === 0) {
            yield [x, y];
            continue;
        }
        for (const xd of range(x - d, x + d)) {
            yield [xd, y - d];
        }
        for (const yd of range(y - d, y + d)) {
            yield [x + d, yd];
        }
        for (const xd of range(x + d, x - d, -1)) {
            yield [xd, y + d];
        }
        for (const yd of range(y + d, y - d, -1)) {
            yield [x - d, yd];
        }
    }
}

export function* spiralIter4d([x, y]: Position, fromDis: number, toDis: number): Generator<Position> {
    for (const d of distanceRange(fromDis, toDis)) {
        if (d === 0) {
            yield [x, y];
            continue;
        }
        for (const i of range(0, d)) {
            yield [x + i, y - d + i]
        }
        for (const i of range(0, d)) {
            yield [x + d - i, y + i]
        }
        for (const i of range(0, d)) {
            yield [x - i, y + d - i]
        }
        for (const i of range(0, d)) {
            yield [x - d + i, y - i]
        }
    }
}

export function* concat<T>(...its: Iterable<T>[]): Generator<T> {
    for (const it of its) {
        for (const v of it) {
            yield v;
        }
    }
}

Array.prototype.last = function () {
    return this[this.length - 1];
};

Array.prototype.equals = function <T>(other: Array<T>): boolean {
    if (this.length !== other.length) {
        return false;
    }
    for (const i in this) {
        if (this[i] !== other[i]) {
            return false;
        }
    }
    return true;
}

export function randomInt(start: number, end: number): number {
    return Math.floor(Math.random() * (end - start + 1)) + start;
}

export function randomChoice<T>(arr: T[]) {
    return arr[Math.floor(Math.random() * arr.length)];
}

export async function asleep(time: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, time));
}